﻿using System;

namespace Silver3D.Math
{
    public struct Vec2
    {
        public static Vec2 Zero { get { return new Vec2(0.0f, 0.0f); } }
        public static Vec2 One { get { return new Vec2(1.0f, 1.0f); } }
        public static Vec2 XAxis { get { return new Vec2(1.0f, 0.0f); } }
        public static Vec2 YAxis { get { return new Vec2(0.0f, 1.0f); } }

        private double x, y;

        public double X { get { return x; } set { x = value; } }
        public double Y { get { return y; } set { y = value; } }

        public double U { get { return x; } set { x = value; } }
        public double V { get { return y; } set { y = value; } }

        public double LengthSquared
        {
            get { return Dot(this); }
        }

        public double Length
        {
            get
            {
                double lsq = LengthSquared;
                if (lsq == 0.0) return 0.0;
                return System.Math.Sqrt(lsq);
            }
        }

        public Vec2 Unit
        {
            get { return new Vec2(this) / Length; }
        }

        public Vec2(double xy)
        {
            x = y = xy;
        }

        public Vec2(double x, double y)
        {
            this.x = x;
            this.y = y;
        }

        public Vec2(Vec2 copy)
            : this(copy.x, copy.y)
        {
        }

        public Vec2(Vec3 v3)
            : this(v3.X, v3.Y)
        {
        }

        public Vec2(Vec4 v4)
            : this(v4.X, v4.Y)
        {
        }

        public double this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0: return x;
                    case 1: return y;
                    default: throw new ArgumentOutOfRangeException("index");
                }
            }
            set
            {
                switch (index)
                {
                    case 0: x = value; break;
                    case 1: y = value; break;
                    default: throw new ArgumentOutOfRangeException("index");
                }
            }
        }

        public static Vec2 operator -(Vec2 v)
        {
            return new Vec2(-v.x, -v.y);
        }

        public static Vec2 operator -(double lhs, Vec2 rhs)
        {
            return new Vec2(lhs) - rhs;
        }

        public static Vec2 operator -(Vec2 lhs, double rhs)
        {
            return lhs - new Vec2(rhs);
        }

        public static Vec2 operator -(Vec2 lhs, Vec2 rhs)
        {
            return new Vec2(lhs.x - rhs.x, lhs.y - rhs.y);
        }

        public static Vec2 operator +(Vec2 v)
        {
            return new Vec2(v);
        }

        public static Vec2 operator +(double lhs, Vec2 rhs)
        {
            return new Vec2(lhs) + rhs;
        }

        public static Vec2 operator +(Vec2 lhs, double rhs)
        {
            return lhs + new Vec2(rhs);
        }

        public static Vec2 operator +(Vec2 lhs, Vec2 rhs)
        {
            return new Vec2(lhs.x + rhs.x, lhs.y + rhs.y);
        }

        public static Vec2 operator *(double s, Vec2 rhs)
        {
            return new Vec2(s * rhs.x, s * rhs.y);
        }

        public static Vec2 operator *(Vec2 lhs, double s)
        {
            return new Vec2(s * lhs.x, s * lhs.y);
        }

        public static Vec2 operator *(Vec2 lhs, Vec2 rhs)
        {
            return new Vec2(lhs.x * rhs.x, lhs.y * rhs.y);
        }

        public static Vec2 operator /(double s, Vec2 rhs)
        {
            double inv = 1.0 / s;
            return inv * rhs;
        }

        public static Vec2 operator /(Vec2 lhs, double s)
        {
            double inv = 1.0 / s;
            return lhs * inv;
        }

        public static Vec2 operator /(Vec2 lhs, Vec2 rhs)
        {
            return new Vec2(lhs.x / rhs.x, lhs.y / rhs.y);
        }

        public static bool operator ==(Vec2 a, Vec2 b)
        {
            return (a.x == b.x) && (a.y == b.y);
        }

        public static bool operator !=(Vec2 a, Vec2 b)
        {
            return (a.x != b.x) || (a.y != b.y);
        }

        public static bool operator <(Vec2 a, Vec2 b)
        {
            return a.LengthSquared < b.LengthSquared;
        }

        public static bool operator >(Vec2 a, Vec2 b)
        {
            return a.LengthSquared > b.LengthSquared;
        }

        public static bool operator <=(Vec2 a, Vec2 b)
        {
            return a.LengthSquared <= b.LengthSquared;
        }

        public static bool operator >=(Vec2 a, Vec2 b)
        {
            return a.LengthSquared >= b.LengthSquared;
        }

        public double Dot(Vec2 rhs)
        {
            return x * rhs.x + y * rhs.y;
        }

        public override string ToString()
        {
            return string.Format("Vec2({0},{1})", x, y);
        }

        public bool IsEquivalent(Vec2 v)
        {
            if (v == null) return false;
            return Util.IsEquivalent(x, v.x) && Util.IsEquivalent(y, v.y);
        }

        public override bool Equals(object obj)
        {
            return (obj != null) && (obj is Vec2) && ((Vec2)obj) == this;
        }

        public override int GetHashCode()
        {
            return x.GetHashCode() ^ y.GetHashCode();
        }
    }
}
